package com.dream.config;

import com.dream.filter.BasicAuthenticationTokenFilter;
import com.dream.filter.JwtAuthenticationFilter;
import com.dream.filter.KaptchaAuthenticationFilter;
import com.dream.filter.OauthAuthenticationFilter;
import com.dream.provider.OauthAuthenticationProvider;
import com.dream.service.UserDetailsServiceImpl;
import com.dream.voter.RoleBasedVoter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.WebExpressionVoter;

import javax.annotation.Resource;
import java.util.Arrays;

/**
 * 由于springboot 2.7.0以后弃用了WebSecurityConfigurerAdapter，所以我们直接采用最新的写法，网上教程很多都是老的写法。
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig{

    @Resource
    private RoleBasedVoter roleBasedVoter;

    @Resource
    private UserDetailsServiceImpl userDetailsService;

    @Resource
    private AuthenticationConfiguration authenticationConfiguration;


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        //基于token，所以不需要csrf防护
        httpSecurity.csrf().disable()
                //基于token，所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                //放行验证码接口
                .antMatchers("/loginImage/imageCode")
                .permitAll()
                .accessDecisionManager(accessDecisionManager()) //自定义授权
                //除上面的所有请求全部需要鉴权认证
                .anyRequest()
                .authenticated()
                .and()
                .addFilter(new JwtAuthenticationFilter(authenticationManager())) //添加表单登录Filter
                .addFilterBefore(new BasicAuthenticationTokenFilter(), JwtAuthenticationFilter.class)//授权，验证token
                .addFilterBefore(new KaptchaAuthenticationFilter("/login", "/loginImage/imageCode"), JwtAuthenticationFilter.class) //添加验证码的Filter,并且要在JWT Filter之前
                .addFilterBefore(new OauthAuthenticationFilter("/oauthLogin",authenticationManager()),  KaptchaAuthenticationFilter.class);//添加单点登录的Filter,在验证Filter之前

        //禁用缓存
        httpSecurity.headers().cacheControl();
        return httpSecurity.build();
    }


    /**
     * 设置密码编码器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    /**
     * 认证管理器，登录的时候参数会传给 authenticationManager
     * @return
     */
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    /**
     * 设置默认认证提供
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        final DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }

    @Bean
    public OauthAuthenticationProvider oauthAuthenticationProvider(AuthenticationManagerBuilder authenticationManagerBuilder) {
        OauthAuthenticationProvider oauthAuthenticationProvider = new OauthAuthenticationProvider();
        //单点登录无需要设置PasswordEncoder，因为不需要密码即可登录
        authenticationManagerBuilder.authenticationProvider(oauthAuthenticationProvider);
        return oauthAuthenticationProvider;
    }

    /**
     * 原有授权之后，增加自定义授权
     * @return
     */
    @Bean
    public AccessDecisionManager accessDecisionManager() {
        java.util.List<AccessDecisionVoter<? extends Object>> decisionVoters
                = Arrays.asList(
                new WebExpressionVoter(),
                new RoleVoter(),
                new AuthenticatedVoter(),
                roleBasedVoter
        );
        return new UnanimousBased(decisionVoters);
    }

}
